﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Drawing;
using GRV11;

namespace gpuimp.core
{
    public class Paintbucket : Tool
    {
        /// <summary>
        /// basic tool constructor
        /// </summary>
        public Paintbucket()
            : base()
        {
            type = ToolType.Paintbucket;
            name = "PaintBucket";

        }

        /// <summary>
        /// create a tool with specified setting already
        /// </summary>
        /// <param name="tool"></param>
        public Paintbucket(Tool tool)
            : base(tool)
        {
            type = ToolType.Paintbucket;
            name = "PaintBucket";
        }


        protected override void setToolShader(PointF position, List<PointF> pastPositions, PointF viewSize, System.Windows.Media.Color color, float pressure)
        {
            GRSupport.SupportShaderProgramSelect(CanvasManager.gr, this.shader);

            int shaderDiffuse = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "diffuseMap");
            int shaderCursor = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "cursorPos");
            int shaderImage = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "imageSize");
            int shaderViewport = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "viewportSize");
            int shaderCanvas = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "canvasPos");
            int shaderColor = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "brushColor");
            int shaderBrushsize = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "brushSize");
            int shaderBrushhardness = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "brushHardness");
            int shaderMask = (int)CanvasManager.gr.glGetUniformLocationARB(this.shader, "selectionMask");
            
            CanvasManager.gr.glUniform2fARB(shaderCursor, position.X, position.Y);
            CanvasManager.gr.glUniform2fARB(shaderImage, CanvasManager.CurrentCanvas.ImageSize.X, CanvasManager.CurrentCanvas.ImageSize.Y);
            CanvasManager.gr.glUniform2fARB(shaderViewport, viewSize.X, viewSize.Y);
            CanvasManager.gr.glUniform2fARB(shaderCanvas, 0, 0); //moving the canvas would go here
            CanvasManager.gr.glUniform1fARB(shaderBrushsize, 10 * pressure);
            CanvasManager.gr.glUniform1fARB(shaderBrushhardness, 100);
            CanvasManager.gr.glUniform4fARB(shaderColor, color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f);
            CanvasManager.gr.glUniform1iARB(shaderDiffuse, 0);
            CanvasManager.gr.glUniform1iARB(shaderMask, 1);
        }

        public override void draw(GRControl control, PointF position, List<PointF> pastPositions, System.Windows.Media.Color color, float pressure)
        {
            Console.WriteLine(position.ToString());
            applying = true;

            //get render control
            GR gr = control.GetGR();
            //height and width of the image
            int activeLayer = CanvasManager.CurrentCanvas.LayerManager.ActiveLayer;
            int width = CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].grBaseTexture.GetWidth();
            int height = CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].grBaseTexture.GetHeight();

            gr.glLoadIdentity();
            //gr.glViewport(0, 0, width, height);

            gr.glPushMatrix();
            //make a new texture - just testing this way
            GRTexture texture = new GRTexture();

            //make empty texture
            //texture.CreateTextureFromBitmap(CanvasManager.gr, new Bitmap(width, height), false);
            //set the paintbrush shader
            setToolShader(position, pastPositions, new PointF(control.Width, control.Height), color, pressure);
            //clear render
            gr.glClear(GR.GL_COLOR_BUFFER_BIT);
            //get IDs of the two textures to use
            //int fboTexture = texture.GetOpenGLTextureID();// CanvasManager.CurrentCanvas.grFBOTexture.GetOpenGLTextureID();
            int fboTexture = CanvasManager.CurrentCanvas.grFBOTexture.GetOpenGLTextureID();
            int layerTexture = CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].grBaseTexture.GetOpenGLTextureID();

            //bind FBO
            gr.glBindFramebufferEXT(GR.GL_FRAMEBUFFER_EXT, CanvasManager.CurrentCanvas.FBO[0]);
            //bind intermediate texture to fbo
            gr.glFramebufferTexture2DEXT(GR.GL_FRAMEBUFFER_EXT, GR.GL_COLOR_ATTACHMENT0_EXT, GR.GL_TEXTURE_2D, fboTexture, 0);

            //set to draw to color 0 buffer
            int[] buffers = { GR.GL_COLOR_ATTACHMENT0_EXT };
            gr.glDrawBuffers(1, buffers);

            //set to render to texture size
            setupToolViewport(gr);

            //set the original texture active
            CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].grBaseTexture.SetAsActiveTexture(gr);
            //texture.SetAsActiveTexture(CanvasManager.gr);
            //draw quad with this texture
            CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].Draw(gr, false, false);

            //unbind FBO
            CanvasManager.gr.glBindFramebufferEXT(GR.GL_FRAMEBUFFER_EXT, 0);
            //remove shader
            GRSupport.SupportShaderProgramSelect(CanvasManager.gr, 0);

            //rebind fbo
            gr.glBindFramebufferEXT(GR.GL_FRAMEBUFFER_EXT, CanvasManager.CurrentCanvas.FBO[1]);
            //bind final output texture
            gr.glFramebufferTexture2DEXT(GR.GL_FRAMEBUFFER_EXT, GR.GL_COLOR_ATTACHMENT0_EXT, GR.GL_TEXTURE_2D, layerTexture, 0);
            //switch to copy texture
            setCopyShader();
            //render wuad with intermediate texture
            //set the intermediate texture active
            //texture.SetAsActiveTexture(gr);
            CanvasManager.CurrentCanvas.grFBOTexture.SetAsActiveTexture(gr);
            CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].Draw(CanvasManager.gr, false, false);

            //unbind FBO
            CanvasManager.gr.glBindFramebufferEXT(GR.GL_FRAMEBUFFER_EXT, 0);
            //remove shader
            GRSupport.SupportShaderProgramSelect(CanvasManager.gr, 0);

            //rebind fbo
            gr.glBindFramebufferEXT(GR.GL_FRAMEBUFFER_EXT, CanvasManager.CurrentCanvas.FBO[1]);
            //bind final output texture
            gr.glFramebufferTexture2DEXT(GR.GL_FRAMEBUFFER_EXT, GR.GL_COLOR_ATTACHMENT0_EXT, GR.GL_TEXTURE_2D, fboTexture, 0);
            //switch to copy texture
            setCopyShader();
            //render wuad with intermediate texture
            //set the intermediate texture active
            CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].grBaseTexture.SetAsActiveTexture(gr);
            CanvasManager.CurrentCanvas.LayerManager.Layers[activeLayer].Draw(CanvasManager.gr, false, false);

            gr.glPopAttrib();
            gr.glPopMatrix();
            //unbind framebuffer
            CanvasManager.gr.glBindFramebufferEXT(GR.GL_FRAMEBUFFER_EXT, 0);
            //turn off shader
            GRSupport.SupportShaderProgramSelect(CanvasManager.gr, 0);
        }

        public override void setFragProg(String frag)
        {
            //   this.fragProg = fragProg;
            this.fragProg = @"
//existing texture
uniform sampler2D diffuseMap;
//selection mask
uniform sampler2D selectionMask;
//color to draw with
uniform vec4 brushColor;
//uv coordinate of quad image is applied to
varying vec2 texCoord;

void main(void)
{
    //get the existing texture
    vec4 diffuse = texture2D(diffuseMap, texCoord);

    float newAlpha = texture2D(selectionMask, texCoord);
    vec4 finalColor = (1 - newAlpha) * diffuse + newAlpha * brushColor;
    float totalAlpha = clamp(diffuse.a + newAlpha, 0.0, 1.0);
    finalColor.a = totalAlpha;
    gl_FragColor = finalColor;
}  
";

        }

        public override void setVertProg(String vert)
        {
            //this.vertProg = vertProg;
            vertProg = @"varying vec2  texCoord;

void main(void)
{
   gl_Position = vec4( gl_Vertex.xy, 0.0, 1.0 );
   gl_Position = sign( gl_Position );
    
   // Texture coordinate for screen aligned (in correct range):
   //texCoord = (vec2( gl_Position.x, - gl_Position.y ) + vec2( 1.0 ) ) / vec2( 2.0 );
   gl_Position = ftransform();
   texCoord = gl_MultiTexCoord0.xy;
}";
        }
    }
}
